iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

不管您是否NBA粉絲,只要對籃球界有點認識,NBA史上傳奇控衛--Magic Johnson魔術強生--的大名,總不會陌生吧?告訴大家,原來Python也有Magic,而且不只一位。Python的magic當然不是Magic Johnson,而是magic objects。講白點就是magic attributesmagic methods

到目前為止,大家應該可以感受得到,OO就是環繞著類別、屬性、方法,還有公開、保護、私有這幾個概念,以及封裝、繼承、多型等幾大支柱轉來轉去。只要搞清楚這些術語的意義和相互關係,一隻腳大概已跨入OO堂奧。

當然,OO水很深,絕對不只以上這些,往後的篇章筆者將傾盡所知所學,介紹更多觀念和Python的OO上的實作。


  • 上篇說過,有前綴雙底線而無後綴雙底線的屬性如__age是私有屬性。沒有明說,大家用腳趾想也猜到的潛台詞是:有前綴雙底線而無後綴雙底線的方法如__do_private_stuff()是私有方法。
  • 那麼,前後都綴雙底線(註1)的屬性和方法,例如__class__, __add__()又是甚麼?答案是:這些長相有點奇怪的屬性和方法,就是剛才說的'magic attributes'以及'magic methods'。中文名稱也很直觀,前面加個「魔術」即可。'magic attributes'是「魔術屬性」,'magic methods'自然就是「魔術方塊」,不,是「魔術方法」(註2)。
  • 這些魔術屬性和方法,都是Python內部物件,PEP 8明文規範:"Never invent such names"。所以,除非是「Python語言本身」的開發者(如Guido van Rossum或其他核心成員),絕大多數僅靠Python糊口混飯吃的芸芸Pythonistas(註3),對這些魔術屬性和方法,了解會用就好,最好自己別嘗試變魔術。不過筆者還是看到有人在變,可能他們想超越劉謙老師吧。
  • PEP 8原文:

    __double_leading_and_trailing_underscore__: “magic” objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. **Never invent such names**; only use them as documented.

  • 下面和往後幾天的範例,基於講解方便,暫不沿用Tree()類別,換用Cult()邪教為例。
    class Cult():  # 邪教
        def __init__(self, founder: str, name: str):
            self.founder = founder  # public
            self.__name = name      #  private
            self.__name_ = "*".join([char for char in name])   # private
            self.__name__ = "+".join([char for char in name])  # 前後綴雙底線,觸犯PEP 8玉律。
    
        def preach(self):  # public活動
            print(".公開宣揚教義。")
    
        def __grab_money_n_imprison(self):  # private偷偷摸摸活動
            print(f"{self.__name=}, {self.__name_=}:非法歛財禁錮信眾。")
    
        def __rape_n_rob_n_kill_(self):
            print(f"{self.__name=}, {self.__name_=}:奸淫擄掠殘殺無辜。")
    
        def __do_evils__(self):  # 以為是private,可為非作歹肆無忌憚,其實早就曝光公諸於世。
            self.__grab_money_n_imprison()
            self.__rape_n_rob_n_kill_()
    
  • 注意:筆者故意在建構子(註4)中,定義了一個魔術屬性__name__ 。這只是為了講解之用,PEP 8是不鼓勵的。真正寫code時應盡量避免。
    # private屬性測試
    cult1 = Cult("麻原彰晃", "奧姆真理教")
    print(f"{cult1.founder=}")
    cult1.founder = "あさはら しょうこう"
    print(f"{cult1.founder=}")
    # print(f'{cult1.__name=}')  # private,無法存取
    # print(f'{cult1.__name_=}')  # private,無法存取
    print(f"{cult1.__name__=}")  # 非private,可以存取,但強烈建議不要變這種戲法。
    cult1.__name__ = "Ōmu Shinrikyō"
    print(f"{cult1.__name__=}")
    
    print("----------------")
    
    # private方法測試
    cult2 = Cult("Jim Jones", "人民聖殿教")
    cult2.preach()
    cult2.__name = "人民殿堂"  # 觀察實際上有無改值。
    cult2.__name_ = "Peoples Temple"  # 觀察實際上有無改值。
    # cult2.__grab_money_n_imprison()
    # cult2.__rape_n_rob_n_kill_()
    cult2.__do_evils__()
    
    輸出:
    https://ithelp.ithome.com.tw/upload/images/20220922/20148485QufTLiT7HL.png
  • 如輸出顯示,__name__前後都有雙底線,成為「魔術屬性」,已經不是私有屬性。可以利用物件.屬性的表示式順利取值和賦值。
  • 同樣道理,__do_evils__是個「魔術方法」,並非私有方法(我猜想其封裝保護層級應為公開),所以可籍由「物件.方法」的表示式去呼叫。而這個方法內,呼叫了兩個私有方法,結果是私有方法的內容曝露於外。如果這不是類別設計者的原意,就是個bug。
  • 補充說明:雙底線__英文稱為dunder(聽發音),是double underscore的縮寫字。

下篇預告:

  • 其實Python是可以在外部修改私有屬性的...怎麼啦,我們不是一直斬釘截鐵說不行嗎?這樣不就亂了套?

註1: 「前後都綴雙底線」的更精準說法是「前後都綴最少雙底線」。意思是前綴3條、4條...n條,或後綴3條、4條...n條都算是magic。不是「剛好」兩條才算。

註2: 以筆者找到的資料,Python的「魔術方法」數量上多於「魔術屬性」。所以前後皆綴雙底線的「魔術師」,大部分是「方法」。

註3: Python社群對Python programmers通常有兩個稱謂:pythonistapythoneer(複數當然就是pythonistas和pythoneers)。只要是使用、愛好Python的人,不管職業或業餘,也不管程度如何,即使是剛入門者,通通可稱為pythonista(s)。至於pythoneer(s),則多是對大師級且對Python語言或社群有貢獻的專家的尊稱。

註4: 建構子__init__()本身也是個魔術方法。


上一篇
內外有別
下一篇
在外部修改私有屬性,真行嗎?
系列文
Oops! OOPP: An Introduction to Object-Oriented Programming in Python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言